﻿using System.Data;
using System.Linq;
using StockholmsStad.Data.Entity;

namespace StockholmsStad.Data
{
    /// <summary>
    /// Implementation av interfacet IDataContextGeneric
    /// </summary>
    internal class DataContextModify : IDataContextModify
    {
        #region IDataContextModify Members

        public int? InsertCeremony(CeremonyView ceremonyView)
        {
            var db = new StockholmsStadDataContext(IsolationLevel.RepeatableRead);
            int? identity = null;
            db.InsertCeremony(ceremonyView.CeremonyLanguage, ceremonyView.CertificateLanguage,
                              ceremonyView.SpecialNeeds, ref identity);
            db.SubmitChanges();
            return identity;
        }

        public int? InsertPerson(PersonView personView)
        {
            var db = new StockholmsStadDataTablesContext();
            int? addressId = InsertAddress(db, personView);
            var person = new Person
                             {
                                 AddressId = addressId,
                                 FirstNames = personView.FirstNames,
                                 LastName = personView.LastName,
                                 Nationality = personView.Nationality,
                                 PersonalNumber = personView.PersonalNumber,
                                 ProtectedIdentity = personView.ProtectedIdentity,
                                 SexMale = personView.SexMale,
                                 Telephone = personView.Telephone,
                             };
            db.Persons.InsertOnSubmit(person);
            db.SubmitChanges();
            return person.PersonId;
        }

        public bool UpdatePerson(PersonView view)
        {
            var db = new StockholmsStadDataTablesContext();
            Person person = (from p in db.Persons
                             where p.PersonId == view.PersonId
                             select p).SingleOrDefault();
            if (person == null)
                return false;
            int? addressId = UpdateAddress(db, person.AddressId, view);
            person.AddressId = addressId;
            person.FirstNames = view.FirstNames;
            person.LastName = view.LastName;
            person.Nationality = view.Nationality;
            person.PersonalNumber = view.PersonalNumber;
            person.ProtectedIdentity = view.ProtectedIdentity;
            person.SexMale = view.SexMale;
            person.Telephone = view.Telephone;
            db.SubmitChanges();
            return true;
        }

        public bool UpdateCeremony(CeremonyView ceremonyView)
        {
            var db = new StockholmsStadDataContext(IsolationLevel.RepeatableRead);
            int rowCount = db.UpdateCeremony(ceremonyView.CeremonyLanguage, ceremonyView.CertificateLanguage,
                                             ceremonyView.SpecialNeeds, ceremonyView.CeremonyId);
            db.SubmitChanges();
            return rowCount != 0;
        }

        public bool RemoveCeremony(int ceremonyId)
        {
            var db = new StockholmsStadDataTablesContext();
            Ceremony ceremony = (from c in db.Ceremonies
                                 where c.CeremonyId == ceremonyId
                                 select c).SingleOrDefault();
            if (ceremony == null)
                return false;
            db.Ceremonies.DeleteOnSubmit(ceremony);
            db.SubmitChanges();
            return true;
        }

        public bool RemovePerson(int personId)
        {
            var db = new StockholmsStadDataTablesContext();
            Person person = (from p in db.Persons
                             where p.PersonId == personId
                             select p).SingleOrDefault();
            if (person == null)
                return false;
            RemoveAddress(db, person.AddressId);
            db.Persons.DeleteOnSubmit(person);
            db.SubmitChanges();
            return true;
        }

        public int InsertCouple(CoupleView view, string password)
        {
            var db = new StockholmsStadDataTablesContext();
            var couple = new Couple
                             {
                                 CeremonyId = view.CeremonyId,
                                 ContactPersonId = view.ContactPersonId,
                                 PersonId1 = view.PersonId1,
                                 PersonId2 = view.PersonId2,
                             };
            db.Couples.InsertOnSubmit(couple);
            db.SubmitChanges();
            view.CoupleId = couple.CoupleId;
            InsertUser(db, view, password);
            return couple.CoupleId;
        }

        public bool RemoveCouple(int coupleId)
        {
            var db = new StockholmsStadDataTablesContext();
            Couple couple = (from p in db.Couples
                             where p.CoupleId == coupleId
                             select p).SingleOrDefault();
            if (couple == null)
            {
                RemoveUser(db, coupleId);
                return false;
            }
            RemoveUser(db, couple.CoupleId);
            db.Couples.DeleteOnSubmit(couple);
            db.SubmitChanges();
            return true;
        }

        public bool UpdateCouple(CoupleView view)
        {
            var db = new StockholmsStadDataTablesContext();
            Couple couple = (from p in db.Couples
                             where p.CoupleId == view.CoupleId
                             select p).SingleOrDefault();
            if (couple == null)
                return false;
            couple.ContactPersonId = view.ContactPersonId;
            couple.PersonId1 = view.PersonId1;
            couple.PersonId2 = view.PersonId2;
            UpdateUserLogin(db, view);
            db.SubmitChanges();
            return true;
        }

        #endregion

        private bool UpdateUserLogin(StockholmsStadDataTablesContext db, CoupleView view)
        {
            UserLogin user = (from u in db.UserLogins
                                   where u.CoupleId == view.CoupleId
                                   select u).SingleOrDefault();
            if (user == null)
                return false;
            user.UserName = view.Email;
            db.SubmitChanges();
            return true;
        }

        private int InsertUser(StockholmsStadDataTablesContext db, CoupleView view, string password)
        {
            int typeRoleId = (from r in db.TypeUserRoles
                              where r.Name.Equals("Couple")
                              select r.UserRoleId).Single();
            var user = new UserLogin
                           {
                               CoupleId = view.CoupleId,
                               Password = password,
                               UserName = view.Email,
                               UserRoleId = typeRoleId,
                           };
            db.UserLogins.InsertOnSubmit(user);
            db.SubmitChanges();
            return user.UserId;
        }

        private bool RemoveUser(StockholmsStadDataTablesContext db, int coupleId)
        {
            UserLogin user = (from a in db.UserLogins
                              where a.CoupleId == coupleId
                              select a).SingleOrDefault();
            if (user != null)
            {
                db.UserLogins.DeleteOnSubmit(user);
                return true;
            }
            return false;
        }

        private int? InsertAddress(StockholmsStadDataTablesContext db, PersonView personView)
        {
            if (IsEmptyAddress(personView))
            {
                // Do not save address
                return null;
            }
            var address = new Address
                              {
                                  CareOf = personView.CareOf,
                                  City = personView.City,
                                  Country = personView.Country,
                                  PostCode = personView.PostCode,
                                  StreetAddress = personView.StreetAddress,
                              };
            db.Addresses.InsertOnSubmit(address);
            db.SubmitChanges();
            return address.AddressId;
        }

        private bool IsEmptyAddress(PersonView personView)
        {
            return string.IsNullOrEmpty(personView.CareOf) &&
                   string.IsNullOrEmpty(personView.City) &&
                   string.IsNullOrEmpty(personView.Country) &&
                   string.IsNullOrEmpty(personView.PostCode) &&
                   string.IsNullOrEmpty(personView.StreetAddress);
        }

        private bool RemoveAddress(StockholmsStadDataTablesContext db, int? addressId)
        {
            if (addressId != null)
            {
                Address address = (from a in db.Addresses
                                   where a.AddressId == addressId
                                   select a).SingleOrDefault();
                if (address != null)
                {
                    db.Addresses.DeleteOnSubmit(address);
                    return true;
                }
            }
            return false;
        }

        private int? UpdateAddress(StockholmsStadDataTablesContext db, int? id, PersonView view)
        {
            bool oldAddressEmpty = id == null;
            bool newAddressEmpty = IsEmptyAddress(view);
            if (oldAddressEmpty && newAddressEmpty)
            {
                // Both new and old doesnt exist
                return null;
            }
            if (!oldAddressEmpty && newAddressEmpty)
            {
                // New address is empty
                RemoveAddress(db, id);
                return null;
            }
            if (oldAddressEmpty && !newAddressEmpty)
            {
                // Old address was empty
                return InsertAddress(db, view);
            }
            // Both old and new exist, do an update
            Address address = (from a in db.Addresses
                               where a.AddressId == id.Value
                               select a).Single();
            address.CareOf = view.CareOf;
            address.City = view.City;
            address.Country = view.Country;
            address.PostCode = view.PostCode;
            address.StreetAddress = view.StreetAddress;
            db.SubmitChanges();
            return id;
        }
    }
}